package cn.icanci.loopstack.ras.client.facade;

import cn.hutool.http.Method;
import cn.icanci.loopstack.api.client.Client;
import cn.icanci.loopstack.api.client.http.HttpClientImpl;
import cn.icanci.loopstack.ras.client.cache.holder.RasClientRepositoryHolder;
import cn.icanci.loopstack.ras.client.exception.RpcCallException;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;

/**
 * 请求说明
 * - relativePath: 指的是暴露服务的路径，比如服务地址 => http://127.0.0.1:8080/ddk/condig/query
 *                 其暴露的服务相对路径为 => /ddk/condig/query
 * 
 * @author icanci
 * @since 1.0 Created in 2023/01/19 19:26
 */
@Service
public class RpcCallFacade {

    private static final Map<String, String> DEFAULT_HEADERS      = Maps.newHashMap();

    private static final Method              DEFAULT_METHOD       = Method.POST;

    private static final int                 DEFAULT_READ_TIMEOUT = 3;

    private static final int                 DEFAULT_RETRY        = 0;

    /**
     * RPC 请求客户端
     */
    private static final Client              CLIENT               = HttpClientImpl.getInstance();

    @Resource
    private RasClientRepositoryHolder        rasClientRepositoryHolder;

    /**
     * Get请求调用
     *
     * @param appId appId
     * @param relativePath 请求相对路径
     * @param request 请求
     * @param headers 请求头
     * @param readTimeOut 请求超时时间(单位 秒)
     * @param retry 重试次数
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T getCall(String appId, String relativePath, Object request, Map<String, String> headers, int readTimeOut, int retry, Class<T> clazz) {
        return call(appId, relativePath, request, headers, Method.GET, readTimeOut, retry, clazz);
    }

    /**
     * post请求调用
     *
     * @param appId appId
     * @param relativePath 请求相对路径
     * @param request 请求
     * @param headers 请求头
     * @param readTimeOut 请求超时时间(单位 秒)
     * @param retry 重试次数
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T postCall(String appId, String relativePath, Object request, Map<String, String> headers, int readTimeOut, int retry, Class<T> clazz) {
        return call(appId, relativePath, request, headers, Method.POST, readTimeOut, retry, clazz);
    }

    /**
     * 默认get请求调用
     *
     * @param appId appId
     * @param relativePath 请求相对路径
     * @param request 请求
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T defaultGetCall(String appId, String relativePath, Object request, Class<T> clazz) {
        return call(appId, relativePath, request, DEFAULT_HEADERS, Method.GET, DEFAULT_READ_TIMEOUT, DEFAULT_RETRY, clazz);
    }

    /**
     * 默认post请求调用
     *
     * @param appId appId
     * @param relativePath 请求相对路径
     * @param request 请求
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T defaultPostCall(String appId, String relativePath, Object request, Class<T> clazz) {
        return call(appId, relativePath, request, DEFAULT_HEADERS, Method.POST, DEFAULT_READ_TIMEOUT, DEFAULT_RETRY, clazz);
    }

    /**
     * 请求调用
     *
     * @param appId appId
     * @param relativePath 请求相对路径
     * @param request 请求
     * @param headers 请求头
     * @param method 请求方法
     * @param readTimeOut 请求超时时间(单位 秒)
     * @param retry 重试次数
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T call(String appId, String relativePath, Object request, Map<String, String> headers, Method method, int readTimeOut, int retry, Class<T> clazz) {
        return reallyCall(appId, resolvingRequestUrl(appId, relativePath), request, headers == null ? DEFAULT_HEADERS : headers, method, readTimeOut, retry, clazz);
    }

    /**
     * 请求调用
     *
     * @param appId appId AOP 使用
     * @param url url
     * @param request 请求
     * @param headers 请求头
     * @param method 请求方法
     * @param readTimeOut 请求超时时间(单位 秒)
     * @param retry 重试次数
     * @param clazz 请求返回类型
     * @param <T> 请求返回泛型
     * @return 返回请求返回数据
     */
    public <T> T reallyCall(String appId, String url, Object request, Map<String, String> headers, Method method, int readTimeOut, int retry, Class<T> clazz) {
        // 构建请求参数
        Client.RpcRequest rpcRequest = new Client.RpcRequest(url, request, headers, method, readTimeOut, TimeUnit.SECONDS, retry);
        // 调用
        return CLIENT.call(rpcRequest, clazz);
    }

    /**
     * 从仓储中获取此次执行的请求数据
     * 
     * @param appId appId
     * @param relativePath 请求相对路径
     * @return 返回需要执行的请求地址
     */
    private String resolvingRequestUrl(String appId, String relativePath) {
        if (StringUtils.isBlank(relativePath)) {
            throw new RpcCallException("The relativePath is null! Please check your config!");
        }

        String prefixUrl = rasClientRepositoryHolder.routingRequestAddress(appId);

        return prefixUrl + relativePath;
    }

}
